package com.bizmda.bizsip.app.service;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.bizmda.bizsip.common.*;
import com.bizmda.bizsip.config.AbstractSinkConfig;
import com.bizmda.bizsip.config.RabbitmqSinkConfig;
import com.bizmda.bizsip.config.RestSinkConfig;
import com.bizmda.bizsip.config.SinkConfigMapping;
import com.bizmda.bizsip.app.config.RabbitmqConfig;
import com.bizmda.bizsip.service.AppLogService;
import com.bizmda.log.trace.MDCTraceUtils;
import com.open.capacity.redis.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;

/**
 * SIP聚合服务API调用用接口
 * @author shizhengye
 */
@Slf4j
@Service
public class AppClientService {
    public static final String PREFIX_SIP_ASYNCLOG = "sip:asynclog:";
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private SinkConfigMapping sinkConfigMapping;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private AppLogService appLogService;

    /**
     * 执行服务适配器服务的调用
     *
     * @param sinkId 服务适配器ID
     * @param inData 传入数据
     * @return 返回数据，为BizMessage格式
     */
    public BizMessage<JSONObject> callSink(String sinkId, Object inData) throws BizException {
        JSONObject jsonObject = JSONUtil.parseObj(inData);

        BizMessage<JSONObject> inMessage = BizTools.bizMessageThreadLocal.get();
        inMessage.setData(jsonObject);

        AbstractSinkConfig sinkConfig = sinkConfigMapping.getSinkConfig(sinkId);
        BizMessage<JSONObject> outMessage;
        if (sinkConfig.getType() == AbstractSinkConfig.TYPE_REST) {
            RestSinkConfig restServerAdaptorConfig = (RestSinkConfig) sinkConfig;
            log.debug("调用同步Sink服务[{}]: {}", sinkId, restServerAdaptorConfig.getUrl());
            log.trace("调用Sink服务[{}]报文:\n{}", sinkId, BizUtils.buildBizMessageLog(inMessage));
            outMessage = restTemplate.postForObject(restServerAdaptorConfig.getUrl(), inMessage, BizMessage.class);
            log.debug("返回:\n{}", BizUtils.buildBizMessageLog(outMessage));
            if (outMessage == null) {
                throw new BizException(BizResultEnum.OTHER_RETURN_NULL);
            }
            else if (!(outMessage.getData() instanceof JSONObject)) {
                outMessage.setData(JSONUtil.parseObj(outMessage.getData()));
            }
            return outMessage;
        } else if (sinkConfig.getType() == AbstractSinkConfig.TYPE_RABBITMQ) {
            RabbitmqSinkConfig rabbitmqSinkConfig = (RabbitmqSinkConfig) sinkConfig;
            log.debug("调用异步Sink服务[{}]: {}", sinkId, rabbitmqSinkConfig.getRoutingKey());
            log.trace("调用Sink服务[{}]报文:\n{}", sinkId, BizUtils.buildBizMessageLog(inMessage));
            this.rabbitTemplate.convertAndSend(rabbitmqSinkConfig.getExchange(),
                    rabbitmqSinkConfig.getRoutingKey(), inMessage,
                    message -> {
                        message.getMessageProperties().setHeader(BizConstant.RABBITMQ_MESSAGE_HEADER_TRACE_ID, MDCTraceUtils.getTraceId());
                        return message;
                    });
            outMessage = BizMessage.buildSuccessMessage(inMessage,null);
            return outMessage;
        }
        else {
            log.error("Sink服务[{}]类型错误:{}",sinkId,sinkConfig.getType());
            return BizMessage.buildFailMessage(inMessage,new BizException(BizResultEnum.SINK_TYPE_IS_ERROR,"Sink服务类型:"+sinkConfig.getType()));
        }

    }

    /**
     * 调用存储转发（SAF）服务
     *
     * @param serviceId         调用的服务ID
     * @param inData            传入数据
     * @param delayMilliseconds 延迟时间列表多参（ms）
     * @return 返回数据，为BizMessage格式
     */
    public BizMessage<JSONObject> callDelayAppService(String serviceId, Object inData, int... delayMilliseconds) {
        log.debug("入参:{},{},{}", serviceId, inData, delayMilliseconds);
        JSONObject jsonObject = JSONUtil.parseObj(inData);

        BizMessage<JSONObject> inMessage = BizTools.bizMessageThreadLocal.get();
        inMessage.setData(jsonObject);

        BizMessage<JSONObject> childBizMessage = BizMessage.createChildTransaction(serviceId, inMessage);

        Map<String, Object> map = new HashMap<>(16);
        map.put("serviceId", serviceId);
        map.put("bizmessage", inMessage);

        map.put("retryCount", 0);
        map.put("delayMilliseconds", delayMilliseconds);
        rabbitTemplate.convertAndSend(RabbitmqConfig.DELAY_SERVICE_EXCHANGE, RabbitmqConfig.DELAY_SERVICE_ROUTING_KEY, map,
                message -> {
                    message.getMessageProperties().setHeader(BizConstant.RABBITMQ_MESSAGE_HEADER_TRACE_ID, MDCTraceUtils.getTraceId());
                    message.getMessageProperties().setDelay(delayMilliseconds.length > 0 ? delayMilliseconds[0] : 0);
                    return message;
                });
        this.appLogService.sendAppSuspendLog(inMessage, childBizMessage);
        log.debug("发送交易挂起日志");
        return childBizMessage;
    }

    /**
     * 恢复异步服务上下文
     *
     * @param transactionKey 异步回调的全局唯一交易索引键
     * @return 异步服务上下文
     */
    public Object loadAsyncContext(String transactionKey) throws BizException {
        log.debug("入参:{}", transactionKey);
        Map<String, Object> map = (Map<String, Object>) this.redisUtil.get(PREFIX_SIP_ASYNCLOG + transactionKey);
        if (map == null) {
            throw new BizException(BizResultEnum.ASYNC_APP_SERVICE_CONTEXT_NOT_FOUND);
        }
        String traceId = (String) map.get("traceId");
        Object context = map.get("context");
        BizMessage<JSONObject> bizMessage = BizTools.bizMessageThreadLocal.get();
        if (bizMessage.getParentTraceId() == null) {
            log.debug("重置parentTraceId:{}", traceId);
            bizMessage.setParentTraceId(traceId);
            BizTools.bizMessageThreadLocal.set(bizMessage);
            log.debug("返回:{}", context);
            return context;
        } else if (bizMessage.getParentTraceId().equals(traceId)) {
            log.debug("返回:{}", context);
            return context;
        } else {
            throw new BizException(BizResultEnum.ASYNC_APP_SERVICE_PARENT_TRANCTION_BINDDING_ERROR);
        }
    }
}
